package com.tl.satoken.service;

import com.tl.satoken.domain.entity.Player;
import com.tl.satoken.netty.WebSocketFrameHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class GameRoomService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private WebSocketFrameHandler webSocketFrameHandler;

    private final Map<String, ReentrantLock> gameRoomLocks = new ConcurrentHashMap<>();
    private final int MAX_PLAYERS_PER_GAME_ROOM = 3;

    public boolean joinGameMatch(Integer heroId, Integer gameId) {
        String lockKey = "game:" + gameId + ":lock";
        ReentrantLock lock = gameRoomLocks.computeIfAbsent(lockKey, k -> new ReentrantLock());
        
        try {
            if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {
                try {
                    return doJoinGameMatch(heroId, gameId);
                } finally {
                    lock.unlock();
                }
            } else {
                log.warn("玩家 {} 加入游戏 {} 匹配失败，正在处理其他请求", heroId, gameId);
                return false;
            }
        } catch (InterruptedException e) {
            log.error("加入游戏匹配时被中断", e);
            Thread.currentThread().interrupt();
            return false;
        } catch (Exception e) {
            log.error("加入游戏匹配失败", e);
            return false;
        }
    }

    private boolean doJoinGameMatch(Integer heroId, Integer gameId) {
        try {
            String heroMatchKey = "game:hero:" + heroId + ":match";
            
            if (Boolean.TRUE.equals(redisTemplate.hasKey(heroMatchKey))) {
                log.warn("玩家 {} 已经在匹配队列中", heroId);
                return false;
            }

            // 查找该游戏下是否有未满的房间
            String availableRoomId = findAvailableRoom(gameId);
            log.debug("玩家 {} 查找游戏 {} 可用房间，结果: {}", heroId, gameId, availableRoomId);
            
            if (availableRoomId == null) {
                // 没有未满房间，创建新房间
                availableRoomId = createNewRoom(gameId);
                log.info("为游戏 {} 创建新房间: {}", gameId, availableRoomId);
            } else {
                // 再次检查房间是否真的可用（防止并发问题）
                String roomKey = "minigame:room:" + availableRoomId;
                Long currentCount = redisTemplate.opsForSet().size(roomKey);
                if (currentCount != null && currentCount >= MAX_PLAYERS_PER_GAME_ROOM) {
                    log.warn("房间 {} 在查找后已满员，重新查找或创建新房间", availableRoomId);
                    // 重新查找可用房间
                    availableRoomId = findAvailableRoom(gameId);
                    if (availableRoomId == null) {
                        availableRoomId = createNewRoom(gameId);
                        log.info("重新为游戏 {} 创建新房间: {}", gameId, availableRoomId);
                    }
                }
            }

            // 加入房间
            String roomKey = "minigame:room:" + availableRoomId;
            redisTemplate.opsForSet().add(roomKey, heroId);
            redisTemplate.opsForValue().set(heroMatchKey, availableRoomId, 30, TimeUnit.MINUTES);
            
            Long roomPlayerCount = redisTemplate.opsForSet().size(roomKey);
            log.info("玩家 {} 加入游戏 {} 房间 {}，当前房间人数: {}", heroId, gameId, availableRoomId, roomPlayerCount);
            
            // 记录倒计时信息
            recordPlayerCountdown(heroId, gameId, availableRoomId);
            
            // 检查房间是否满员，如果满员则通知小房间里面的玩家游戏开始
            if (roomPlayerCount != null && roomPlayerCount.intValue() == MAX_PLAYERS_PER_GAME_ROOM) {
                startGame(availableRoomId, gameId);
            }
            
            return true;
        } catch (Exception e) {
            log.error("执行加入游戏匹配逻辑失败", e);
            return false;
        }
    }

    private String findAvailableRoom(Integer gameId) {
        try {
            // 查找该游戏下所有房间
            String pattern = "minigame:room:" + gameId + "_*";
            Set<String> roomKeys = redisTemplate.keys(pattern);

            String bestRoomId = null;
            int maxPlayerCount = -1;

            if (roomKeys != null) {
                log.debug("游戏 {} 找到 {} 个房间", gameId, roomKeys.size());

                for (String roomKey : roomKeys) {
                    String keyWithoutPrefix = roomKey.substring("minigame:room:".length());
                    if (keyWithoutPrefix.contains(":")) {
                        continue;
                    }
                    Long playerCount = redisTemplate.opsForSet().size(roomKey);
                    if (playerCount != null && playerCount < MAX_PLAYERS_PER_GAME_ROOM) {
                        // 检查房间是否已开始游戏
                        String roomId = keyWithoutPrefix;
                        if (!isRoomGameStarted(roomId)) {
                            log.debug("房间 {} 当前人数: {}, 最大人数限制: {}", roomId, playerCount, MAX_PLAYERS_PER_GAME_ROOM);
                            // 选择人数最多的房间
                            if (playerCount.intValue() > maxPlayerCount) {
                                maxPlayerCount = playerCount.intValue();
                                bestRoomId = roomId;
                                log.debug("选择房间 {} 作为最佳房间，人数: {}", roomId, playerCount);
                            }
                        } else {
                            log.debug("房间 {} 已开始游戏，跳过", roomId);
                        }
                    } else {
                        log.debug("房间 {} 已满员，人数: {}", roomKey, playerCount);
                    }
                }
            }

            log.debug("游戏 {} 最终选择的房间: {}, 人数: {}", gameId, bestRoomId, maxPlayerCount);

            return bestRoomId;
        } catch (Exception e) {
            log.error("查找可用房间失败", e);
            return null;
        }
    }

    private String createNewRoom(Integer gameId) {
        try {
            // 生成房间ID：游戏ID_房间序号
            String roomCounterKey = "game:" + gameId + ":room_counter";
            if (!Boolean.TRUE.equals(redisTemplate.hasKey(roomCounterKey))) {
                redisTemplate.opsForValue().set(roomCounterKey, 0L);
            }
            
            Long roomNumber = redisTemplate.opsForValue().increment(roomCounterKey);
            String roomId = gameId + "_" + roomNumber;
            String roomKey = "minigame:room:" + roomId;
            
            // 设置房间过期时间
            redisTemplate.expire(roomKey, 3L, TimeUnit.DAYS);
            redisTemplate.expire(roomCounterKey, 3L, TimeUnit.DAYS);
            
            log.info("为游戏 {} 创建新房间: {}", gameId, roomId);
            return roomId;
        } catch (Exception e) {
            log.error("创建新房间失败", e);
            return gameId + "_" + System.currentTimeMillis();
        }
    }

    private boolean isRoomGameStarted(String roomId) {
        try {
            String gameStartKey = "minigame:room:" + roomId + ":started";
            return Boolean.TRUE.equals(redisTemplate.hasKey(gameStartKey));
        } catch (Exception e) {
            log.error("检查房间游戏状态失败", e);
            return false;
        }
    }

    public boolean cancelGameMatch(Integer heroId, Integer gameId) {
        try {
            String heroMatchKey = "game:hero:" + heroId + ":match";
            
            Object storedRoomId = redisTemplate.opsForValue().get(heroMatchKey);
            if (storedRoomId == null) {
                log.warn("玩家 {} 不在任何匹配队列中", heroId);
                return false;
            }

            String roomId = storedRoomId.toString();
            String roomKey = "minigame:room:" + roomId;
            
            // 清除玩家的倒计时记录
            String countdownKey = "minigame:player:" + heroId + ":countdown";
            redisTemplate.delete(countdownKey);
            
            // 从房间中移除玩家
            redisTemplate.opsForSet().remove(roomKey, heroId);
            redisTemplate.delete(heroMatchKey);
            
            Long remainingCount = redisTemplate.opsForSet().size(roomKey);
            log.info("玩家 {} 取消游戏 {} 房间 {} 匹配，剩余房间人数: {}", heroId, gameId, roomId, remainingCount);
            
            // 如果房间为空，删除房间
            if (remainingCount != null && remainingCount == 0) {
                redisTemplate.delete(roomKey);
                log.info("房间 {} 已空，删除房间", roomId);
            }
            
            return true;
        } catch (Exception e) {
            log.error("取消游戏匹配失败", e);
            return false;
        }
    }

    private void startGame(String roomId, Integer gameId) {
        try {
            String roomKey = "minigame:room:" + roomId;
            Set<Object> playerIds = redisTemplate.opsForSet().members(roomKey);
            
            if (playerIds == null || playerIds.size() != MAX_PLAYERS_PER_GAME_ROOM) {
                log.warn("房间 {} 玩家数量不正确，无法开始游戏", roomId);
                return;
            }

            // 清除房间所有玩家的倒计时记录
            clearRoomCountdown(roomId);

            List<Integer> playerIdList = new ArrayList<>();
            for (Object playerId : playerIds) {
                if (playerId instanceof Integer) {
                    playerIdList.add((Integer) playerId);
                    String heroMatchKey = "game:hero:" + playerId + ":match";
                    redisTemplate.delete(heroMatchKey);
                }
            }

            // 检查并处理金老鼠玩家的金币爆炸
            handleMousePlayerCoinBoom(playerIdList);
            
            // 标记房间游戏已开始
            String gameStartKey = "minigame:room:" + roomId + ":started";
            redisTemplate.opsForValue().set(gameStartKey, true, 3L, TimeUnit.DAYS);
            
            log.info("房间 {} 游戏 {} 开始，玩家列表: {}", roomId, gameId, playerIdList);
            
            webSocketFrameHandler.broadcastGameStart(gameId, roomId, playerIdList);
        } catch (Exception e) {
            log.error("开始游戏失败", e);
        }
    }

    public void startGameByCountdown(String roomId, Integer gameId) {
        try {
            String roomKey = "minigame:room:" + roomId;
            Set<Object> playerIds = redisTemplate.opsForSet().members(roomKey);
            
            if (playerIds == null || playerIds.isEmpty()) {
                log.warn("房间 {} 为空，无法开始游戏", roomId);
                return;
            }

            // 清除房间所有玩家的倒计时记录
            clearRoomCountdown(roomId);

            List<Integer> playerIdList = new ArrayList<>();
            for (Object playerId : playerIds) {
                if (playerId instanceof Integer) {
                    playerIdList.add((Integer) playerId);
                    String heroMatchKey = "game:hero:" + playerId + ":match";
                    redisTemplate.delete(heroMatchKey);
                }
            }

            // 检查并处理金老鼠玩家的金币爆炸
            handleMousePlayerCoinBoom(playerIdList);
            
            // 标记房间游戏已开始
            String gameStartKey = "minigame:room:" + roomId + ":started";
            redisTemplate.opsForValue().set(gameStartKey, true, 3L, TimeUnit.DAYS);
            
            log.info("房间 {} 游戏 {} 因倒计时到期开始，玩家列表: {}", roomId, gameId, playerIdList);
            
            webSocketFrameHandler.broadcastGameStart(gameId, roomId, playerIdList);
        } catch (Exception e) {
            log.error("因倒计时开始游戏失败", e);
        }
    }

    public List<Integer> getGameMatchPlayers(Integer gameId) {
        try {
            List<Integer> allPlayers = new ArrayList<>();
            
            // 查找该游戏下所有房间
            String pattern = "minigame:room:" + gameId + "_*";
            Set<String> roomKeys = redisTemplate.keys(pattern);
            
            if (roomKeys != null) {
                for (String roomKey : roomKeys) {
                    String roomId = roomKey.substring("minigame:room:".length());
                    // 只统计未开始游戏的房间
                    if (!isRoomGameStarted(roomId)) {
                        Set<Object> playerIds = redisTemplate.opsForSet().members(roomKey);
                        if (playerIds != null) {
                            for (Object playerId : playerIds) {
                                if (playerId instanceof Integer) {
                                    allPlayers.add((Integer) playerId);
                                }
                            }
                        }
                    }
                }
            }
            
            return allPlayers;
        } catch (Exception e) {
            log.error("获取游戏匹配玩家列表失败", e);
            return new ArrayList<>();
        }
    }

    public Integer getGameMatchCount(Integer gameId) {
        try {
            int totalCount = 0;
            
            // 查找该游戏下所有房间
            String pattern = "minigame:room:" + gameId + "_*";
            Set<String> roomKeys = redisTemplate.keys(pattern);
            
            if (roomKeys != null) {
                for (String roomKey : roomKeys) {
                    String roomId = roomKey.substring("minigame:room:".length());
                    // 只统计未开始游戏的房间
                    if (!isRoomGameStarted(roomId)) {
                        Long count = redisTemplate.opsForSet().size(roomKey);
                        if (count != null) {
                            totalCount += count.intValue();
                        }
                    }
                }
            }
            
            return totalCount;
        } catch (Exception e) {
            log.error("获取游戏匹配人数失败", e);
            return 0;
        }
    }

    public boolean isPlayerInMatch(Integer heroId) {
        try {
            String heroMatchKey = "game:hero:" + heroId + ":match";
            return Boolean.TRUE.equals(redisTemplate.hasKey(heroMatchKey));
        } catch (Exception e) {
            log.error("检查玩家是否在匹配中失败", e);
            return false;
        }
    }

    public Integer getPlayerMatchGameId(Integer heroId) {
        try {
            String heroMatchKey = "game:hero:" + heroId + ":match";
            Object roomId = redisTemplate.opsForValue().get(heroMatchKey);
            if (roomId != null) {
                // 从房间ID中提取游戏ID（格式：gameId_roomNumber）
                String roomIdStr = roomId.toString();
                String[] parts = roomIdStr.split("_");
                if (parts.length >= 1) {
                    return Integer.parseInt(parts[0]);
                }
            }
            return null;
        } catch (Exception e) {
            log.error("获取玩家匹配游戏ID失败", e);
            return null;
        }
    }

    public String getPlayerMatchRoomId(Integer heroId) {
        try {
            String heroMatchKey = "game:hero:" + heroId + ":match";
            Object roomId = redisTemplate.opsForValue().get(heroMatchKey);
            return roomId != null ? roomId.toString() : null;
        } catch (Exception e) {
            log.error("获取玩家匹配房间ID失败", e);
            return null;
        }
    }

    public void endGame(Integer gameId, List<Integer> playerIds) {
        try {
            log.info("游戏 {} 结束，玩家列表: {}", gameId, playerIds);
        } catch (Exception e) {
            log.error("处理游戏结束失败", e);
        }
    }

    public List<String> getGameRooms(Integer gameId) {
        try {
            List<String> roomIds = new ArrayList<>();
            String pattern = "minigame:room:" + gameId + "_*";
            Set<String> roomKeys = redisTemplate.keys(pattern);
            
            if (roomKeys != null) {
                for (String roomKey : roomKeys) {
                    String roomId = roomKey.substring("minigame:room:".length());
                    roomIds.add(roomId);
                }
            }
            
            return roomIds;
        } catch (Exception e) {
            log.error("获取游戏房间列表失败", e);
            return new ArrayList<>();
        }
    }

    public List<Integer> getRoomPlayers(String roomId) {
        try {
            String roomKey = "minigame:room:" + roomId;
            Set<Object> playerIds = redisTemplate.opsForSet().members(roomKey);
            
            if (playerIds == null) {
                return new ArrayList<>();
            }
            
            List<Integer> playerIdList = new ArrayList<>();
            for (Object playerId : playerIds) {
                if (playerId instanceof Integer) {
                    playerIdList.add((Integer) playerId);
                }
            }
            
            return playerIdList;
        } catch (Exception e) {
            log.error("获取房间玩家列表失败", e);
            return new ArrayList<>();
        }
    }

    public Integer getRoomPlayerCount(String roomId) {
        try {
            String roomKey = "minigame:room:" + roomId;
            Long count = redisTemplate.opsForSet().size(roomKey);
            return count != null ? count.intValue() : 0;
        } catch (Exception e) {
            log.error("获取房间玩家数量失败", e);
            return 0;
        }
    }

    public boolean isRoomFull(String roomId) {
        try {
            return getRoomPlayerCount(roomId) >= MAX_PLAYERS_PER_GAME_ROOM;
        } catch (Exception e) {
            log.error("检查房间是否满员失败", e);
            return false;
        }
    }

    private void recordPlayerCountdown(Integer heroId, Integer gameId, String roomId) {
        try {
            String countdownKey = "minigame:player:" + heroId + ":countdown";
            long currentTime = System.currentTimeMillis();
            long expireTime = currentTime + 30000; // 30秒后到期
            
            Map<String, Object> countdownData = new java.util.HashMap<>();
            countdownData.put("gameId", gameId);
            countdownData.put("roomId", roomId);
            countdownData.put("startTime", currentTime);
            countdownData.put("expireTime", expireTime);
            
            redisTemplate.opsForValue().set(countdownKey, countdownData, 35, TimeUnit.SECONDS);
            log.info("玩家 {} 倒计时已记录，游戏ID: {}, 房间ID: {}, 到期时间: {}", 
                heroId, gameId, roomId, expireTime);
        } catch (Exception e) {
            log.error("记录玩家倒计时失败", e);
        }
    }

    private void clearRoomCountdown(String roomId) {
        try {
            String roomKey = "minigame:room:" + roomId;
            Set<Object> playerIds = redisTemplate.opsForSet().members(roomKey);
            
            if (playerIds != null) {
                for (Object playerId : playerIds) {
                    String countdownKey = "minigame:player:" + playerId + ":countdown";
                    redisTemplate.delete(countdownKey);
                }
                log.info("已清除房间 {} 所有玩家的倒计时记录", roomId);
            }
        } catch (Exception e) {
            log.error("清除房间倒计时记录失败", e);
        }
    }

    private void handleMousePlayerCoinBoom(List<Integer> playerIds) {
        try {
            for (Integer heroId : playerIds) {
                // 检查玩家是否为金老鼠
                String playerHashKey = "game:player:" + heroId + ":data";
                Object isMouseObj = redisTemplate.opsForHash().get(playerHashKey, "isMouse");
                
                if (isMouseObj != null && (Boolean) isMouseObj) {
                    log.info("检测到金老鼠玩家 {} 进入小游戏，处理金币爆炸", heroId);
                    
                    // 获取玩家当前所在的金币广场房间
                    String heroRoomKey = "game:hero:" + heroId;
                    Object roomIdObj = redisTemplate.opsForValue().get(heroRoomKey);
                    
                    if (roomIdObj != null) {
                        String plazaRoomId = roomIdObj.toString();
                        
                        // 获取玩家身上的金币信息
                        List<Map<String, Object>> coinBoomData = getPlayerCoinBoomData(heroId);
                        
                        if (!coinBoomData.isEmpty()) {
                            // 向金币广场同房间的其他玩家广播金币爆炸消息
                            webSocketFrameHandler.broadcastCoinBoom(plazaRoomId, heroId, coinBoomData);
                            
                            // 清除玩家身上的金币数据
                            clearPlayerCoins(heroId);
                            
                            log.info("金老鼠玩家 {} 的金币已爆炸，向广场房间 {} 广播消息，金币数量: {}", 
                                heroId, plazaRoomId, coinBoomData.size());
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("处理金老鼠玩家金币爆炸失败", e);
        }
    }

    private List<Map<String, Object>> getPlayerCoinBoomData(Integer heroId) {
        List<Map<String, Object>> coinBoomData = new ArrayList<>();
        try {
            // 直接查询金老鼠身上剩余的金币数量
            String mouseCoinKey = "game:mouse:" + heroId + ":coins";
            Object mouseCoinObj = redisTemplate.opsForValue().get(mouseCoinKey);
            
            if (mouseCoinObj != null) {
                Integer mouseCoinCount = (Integer) mouseCoinObj;
                log.info("金老鼠玩家 {} 身上剩余金币数量: {}", heroId, mouseCoinCount);
                
                // 根据金币数量生成对应的金币数据结构
                for (int i = 0; i < mouseCoinCount; i++) {
                    Map<String, Object> coinData = new HashMap<>();
                    coinData.put("position", i);
                    coinData.put("id", i);
                    coinBoomData.add(coinData);
                }
            }
        } catch (Exception e) {
            log.error("获取金老鼠玩家 {} 金币爆炸数据失败", heroId, e);
        }
        return coinBoomData;
    }

    private void clearPlayerCoins(Integer heroId) {
        try {
            // 清除金老鼠身上的金币数据
            String mouseCoinKey = "game:mouse:" + heroId + ":coins";
            redisTemplate.delete(mouseCoinKey);
            
            log.info("已清除金老鼠玩家 {} 的金币数据", heroId);
        } catch (Exception e) {
            log.error("清除金老鼠玩家 {} 金币数据失败", heroId, e);
        }
    }
}
